package org.durandj.ystream.server.util;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintStream;
import java.security.InvalidParameterException;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
import java.util.logging.Logger;

/**
 * This gathers and stores settings from the settings file. Settings can be retrieved from this
 * class rather easily if you know what the name of the setting is. A list of the settings can
 * also be gotten if you don't know what settings there are.
 * 
 * @author durandj
 * @version 1.0
 *
 */
public class Settings
{
	// TODO: There may be some synchronization issues in here...
	
	/**
	 * Logs runtime information.
	 */
	private static final Logger log = Logger.getLogger(Settings.class.getSimpleName());
	
	/**
	 * This class is using the singleton design to prevent multiple read/writes to the
	 * settings file.
	 */
	private static final Settings singleton = new Settings();
	
	/**
	 * Gets a reference to the settings.
	 * 
	 * @return settings reference
	 */
	public static Settings getSettings()
	{
		return singleton;
	}
	
	// TODO: This may need to be adjusted for handling the jar form...
	/**
	 * The expected path of the settings file.
	 */
	private static final String SETTINGS_PATH = "./settings.conf";
	
	/**
	 * The settings and the values of those settings.
	 */
	private Map<String, String> settings;
	
	/**
	 * Creates the settings structure and loads the settings from the file.
	 */
	private Settings()
	{
		settings = new HashMap<String, String>();
		
		load();
	}
	
	/**
	 * Used to load settings from the settings file.
	 */
	private synchronized void load()
	{
		settings.clear();
		
		File settingsFile = new File(SETTINGS_PATH);
		
		// Check if the exists and if not make it!
		if(!settingsFile.exists())
		{
			log.info("Settings file does not exist. Creating one now...");
			try
			{
				settingsFile.createNewFile();
			}
			catch(IOException e)
			{
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		
		Scanner reader = null;
		try
		{
			reader = new Scanner(settingsFile);
		}
		catch(FileNotFoundException e)
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		log.info("Reading settings from file...");
		while(reader.hasNextLine())
		{
			String line = reader.nextLine();
			
			if(line.isEmpty())
				continue;
			
			String tokens[] = line.split("=");
			
			// Verify that all the information seems to be there...
			if(tokens.length < 2 ||
			   tokens[0].isEmpty() || tokens[1].isEmpty())
			{
				log.warning("Bad line in settings file. Missing information.\n" +
							"\t" + line);
				continue;
			}
			else if(tokens.length > 2)
			{
				log.warning("Bad line in settings file. Too much information.\n" +
				            "\t" + line);
				continue;
			}
			
			// The line has been verified so lets store the information.
			settings.put(tokens[0], tokens[1]);
		}
		log.info("Finished reading file.");
		
		reader.close();
		
		// TODO: loaded.notifyAll();
	}
	
	/**
	 * Reloads the settings file into memory.
	 */
	public void reload()
	{
		load();
	}
	
	/**
	 * Retrieves the value of a particular setting.
	 * 
	 * @param name - the name of the setting
	 * @return returns the value of the setting or null if the setting does not exist
	 */
	public String getSetting(String name)
	{
		// TODO: There should be a check and a wait for the settings to be loaded if they aren't.
		
		if(name == null || name.isEmpty())
			throw new InvalidParameterException("Must give the name of a setting.");
		
		return settings.get(name);
	}
	
	/**
	 * Gets the names of each of the settings in the file.
	 * 
	 * @return the list of the settings in the file
	 */
	public String[] getSettingsList()
	{
		// TODO: There should be a check and a wait for the settings to be loaded if they aren't.
		
		// Lets convert the array from an object array to a string array.
		String ret[] = new String[settings.keySet().size()];
		for(int i = 0; i != settings.keySet().size(); i++)
			ret[i] = (String)settings.keySet().toArray()[i];
		
		return ret;
	}
	
	/**
	 * Changes the value of a setting from the file.
	 * 
	 * @param name - the name of the setting
	 * @param value - the new value of the setting
	 */
	public void setSetting(String name, String value)
	{
		// TODO: There should be a check and a wait for the settings to be loaded if they aren't.
		
		if(name == null || name.isEmpty())
			throw new InvalidParameterException("Must give the name of a setting.");
		
		if(value == null || value.isEmpty())
			throw new InvalidParameterException("Must give a setting value.");
		
		settings.put(name, value);
		writeSettings();
	}
	
	/**
	 * Adds a new setting to the setting to the settings file.
	 * 
	 * @param name - the name of the new setting
	 * @param value - the value of the setting
	 */
	public void addSetting(String name, String value)
	{
		// TODO: There should be a check and a wait for the settings to be loaded if they aren't.
		
		if(name == null || name.isEmpty())
			throw new InvalidParameterException("Must give the name of a setting.");
		
		if(value == null || value.isEmpty())
			throw new InvalidParameterException("Must give a setting value.");
		
		settings.put(name, value);
		writeSettings();
	}
	
	/**
	 * Removes a setting from the file.
	 * 
	 * @param name - the name of the setting
	 */
	public void removeSetting(String name)
	{
		// TODO: There should be a check and a wait for the settings to be loaded if they aren't.
		
		if(name == null || name.isEmpty())
			throw new InvalidParameterException("Must give the name of a setting.");
		
		settings.remove(name);
		writeSettings();
	}
	
	/**
	 * Used to write the current settings to the file.
	 */
	private void writeSettings()
	{
		// TODO: There should be a check and a wait for the settings to be loaded if they aren't.
		
		PrintStream ps = null;
		
		try
		{
			ps = new PrintStream(new File(SETTINGS_PATH));
		}
		catch(FileNotFoundException e)
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		String settingsList[] = getSettingsList();
		for(String s : settingsList)
			ps.println(s + "=" + settings.get(s));
	}
}

